package com.tsd.common.service.impl;

import cn.hutool.core.net.url.UrlBuilder;
import com.alibaba.fastjson.JSON;
import com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayOrderCloseResult;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.tsd.common.service.CommonWxPayService;
import com.tsd.common.vo.WxPayParamsVo;
import com.tsd.core.utils.HlpUtils;
import com.tsd.core.vo.ErrorCode;
import com.tsd.core.vo.HttpResult;
import com.tsd.service.BaseServiceImpl;
import com.tsd.system.vo.WxPaySetting;
import com.tsd.core.utils.DateUtil;
import com.tsd.wxpay.dao.WeixinPayNotifyLogMapper;
import com.tsd.wxpay.entity.WeixinPayNotifyLogExt;
import com.tsd.wxpay.utils.WxPayUtils;
import com.tsd.wxpay.vo.SceneInfoVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;

/**
 * @ClassName: BaseWxPayServiceImpl
 * @Description: TODO
 * @Author: Hillpool
 * @Date: 2022/7/14 11:31
 * @Version: 1.0
 */
@Service
public class CommonWxPayServiceImpl extends BaseServiceImpl implements CommonWxPayService {

    private static final Logger LOGGER = LoggerFactory.getLogger(CommonWxPayServiceImpl.class);

    @Autowired
    private WxPayService wxPayService;
    @Resource
    private WeixinPayNotifyLogMapper weixinPayNotifyLogMapper;

    @Override
    public HttpResult<Object> procWxOrder(String bizSid, Date createDate, WxPayConfig config) throws WxPayException {
        WeixinPayNotifyLogExt logExt = weixinPayNotifyLogMapper.selectLatestByBizSid(bizSid);
        if (logExt == null) {
            logExt = weixinPayNotifyLogMapper.selectByOutTradeNo(bizSid);
        }
        if (logExt == null) {
            return null;
        }
        if (HlpUtils.isEmpty(logExt.getCreate_date())) {
            logExt.setCreate_date(createDate);
        }
        if (HlpUtils.isEmpty(logExt.getBiz_sid())) {
            logExt.setBiz_sid(bizSid);
        }
        wxPayService.setConfig(config);
        // 1、查询微信中订单是否已存在
        WxPayOrderQueryResult queryResult = wxPayService.queryOrder(null, logExt.getOut_trade_no());
        LOGGER.error("查询订单返回：{}", JSON.toJSONString(queryResult));
        // 订单已支付
        if (WxPayConstants.WxpayTradeStatus.SUCCESS.equals(queryResult.getTradeState()) || WxPayConstants.WxpayTradeStatus.REFUND.equals(queryResult.getTradeState())) {
            logExt.update(queryResult);
            weixinPayNotifyLogMapper.updateByPrimaryKey(logExt);
            HttpResult<Object> result = HttpResult.fail(ErrorCode.CODE_PROC_ERROR, "订单已支付");
            result.setData(logExt.getId());
            return result;
        }
        // 判断是否切换支付方式
        if (!config.getTradeType().equals(logExt.getTrade_type())) {
            // 切换支付方式
            if (WxPayConstants.WxpayTradeStatus.NOTPAY.equals(queryResult.getTradeState())) {
                logExt.update(queryResult);
                WxPayOrderCloseResult closeResult = wxPayService.closeOrder(logExt.getOut_trade_no());
                logExt.setReturn_msg(closeResult.getResultMsg());
                logExt.setTrade_state(WxPayConstants.WxpayTradeStatus.CLOSED);
                weixinPayNotifyLogMapper.updateByPrimaryKey(logExt);
            }
        } else {
            logExt.update(queryResult);
            if (WxPayConstants.TradeType.MWEB.equals(config.getTradeType())) {
                // 订单已存在：未支付并且距离创建时间小于5分钟
                double minute = DateUtil.minuteSub(new Date(), logExt.getCreate_date());
                if (WxPayConstants.WxpayTradeStatus.NOTPAY.equals(queryResult.getTradeState()) && logExt.getApp_id().equals(config.getAppId()) && minute < 5) {
                    WxPayMwebOrderResult result = new WxPayMwebOrderResult(logExt.getUrl());
                    return HttpResult.success(result);
                }
            } else {
                // 订单已存在：未支付并且距离创建时间小于2小时
                double hour = DateUtil.hourSub(new Date(), logExt.getCreate_date());
                if (WxPayConstants.WxpayTradeStatus.NOTPAY.equals(queryResult.getTradeState()) && logExt.getApp_id().equals(config.getAppId()) && hour < 2) {
                    if (WxPayConstants.TradeType.JSAPI.equals(config.getTradeType())) {
                        WxPayMpOrderResult result = WxPayUtils.createJsApiSign(logExt, config);
                        return HttpResult.success(result);
                    } else if (WxPayConstants.TradeType.NATIVE.equals(config.getTradeType())) {
                        WxPayNativeOrderResult result = new WxPayNativeOrderResult(logExt.getUrl());
                        return HttpResult.success(result);
                    } else if (WxPayConstants.TradeType.APP.equals(config.getTradeType())) {
                        WxPayAppOrderResult result = WxPayUtils.createAppSign(logExt, config);
                        return HttpResult.success(result);
                    }
                }
            }

            // 订单未关闭
            if (WxPayConstants.WxpayTradeStatus.NOTPAY.equals(queryResult.getTradeState()) || WxPayConstants.WxpayTradeStatus.USER_PAYING.equals(queryResult.getTradeState())) {
                WxPayOrderCloseResult closeResult = wxPayService.closeOrder(logExt.getOut_trade_no());
                logExt.setReturn_msg(closeResult.getReturnMsg());
                logExt.setTrade_state(WxPayConstants.WxpayTradeStatus.CLOSED);
            }

            weixinPayNotifyLogMapper.updateByPrimaryKey(logExt);
        }
        return HttpResult.fail(ErrorCode.CODE_INNER_ERROR, "regen");
    }

    @Override
    public HttpResult<Object> createOrder(WxPayParamsVo paramsVo, WxPaySetting setting, HttpServletRequest request) throws Exception {
        WxPayConfig config = setting.getConfig();
        wxPayService.setConfig(config);
        if (WxPayConstants.TradeType.JSAPI.equals(config.getTradeType())) {
            WxPayMpOrderResult result = this.createJsApiOrder(paramsVo, setting, request);
            return HttpResult.success(result);
        } else if (WxPayConstants.TradeType.MWEB.equals(config.getTradeType())) {
            WxPayMwebOrderResult result = this.createH5Order(paramsVo, setting, request);
            return HttpResult.success(result);
        } else if (WxPayConstants.TradeType.NATIVE.equals(config.getTradeType())) {
            WxPayNativeOrderResult result = this.createNativeOrder(paramsVo, setting, request);
            return HttpResult.success(result);
        } else if (WxPayConstants.TradeType.APP.equals(config.getTradeType())) {
            WxPayAppOrderResult result = this.createAppOrder(paramsVo, setting, request);
            return HttpResult.success(result);
        }
        return null;
    }

    private WxPayMpOrderResult createJsApiOrder(WxPayParamsVo paramsVo, WxPaySetting setting, HttpServletRequest request) throws Exception {
        WxPayConfig config = setting.getConfig();

        WxPayUnifiedOrderRequest orderRequest = WxPayUtils.createRequest(paramsVo, request);
        WxPayMpOrderResult result = wxPayService.createOrder(orderRequest);
        // 缓存prepayID用于后续模版通知
        String prepayId = result.getPackageValue();
        prepayId = prepayId.replace("prepay_id=", "");

        WeixinPayNotifyLogExt logExt = new WeixinPayNotifyLogExt();
        logExt.setBiz_type(paramsVo.getBiz_type());
        logExt.setBiz_sid(paramsVo.getBiz_sid());
        logExt.setOut_trade_no(paramsVo.getOut_trade_no());
        logExt.setTrade_type(config.getTradeType());
        logExt.setPrepay_id(prepayId);
        logExt.setNonce_str(result.getNonceStr());
        logExt.setApp_id(result.getAppId());

        logExt.setCreate_date(new Date());
        weixinPayNotifyLogMapper.insertSelective(logExt);
        return result;
    }

    private WxPayMwebOrderResult createH5Order(WxPayParamsVo paramsVo, WxPaySetting setting, HttpServletRequest request) throws Exception {
        WxPayConfig config = setting.getConfig();

        WxPayUnifiedOrderRequest orderRequest = WxPayUtils.createRequest(paramsVo, request);
        orderRequest.setOpenid(null);
        SceneInfoVo infoVo = SceneInfoVo.genWap(setting.getWxpay_h5_wap_url(), setting.getWxpay_h5_wap_name());
        orderRequest.setSceneInfo(infoVo.toJsonString());
        WxPayMwebOrderResult result = wxPayService.createOrder(orderRequest);

        WeixinPayNotifyLogExt logExt = new WeixinPayNotifyLogExt();
        logExt.setBiz_type(paramsVo.getBiz_type());
        logExt.setBiz_sid(paramsVo.getBiz_sid());
        logExt.setOut_trade_no(paramsVo.getOut_trade_no());
        logExt.setTrade_type(config.getTradeType());
        logExt.setApp_id(config.getAppId());
        logExt.setMch_id(config.getMchId());
        logExt.setUrl(result.getMwebUrl());
        // 解析 mwebUrl
        UrlBuilder builder = UrlBuilder.of(result.getMwebUrl());
        CharSequence prepayId = builder.getQuery().get("prepay_id");
        if (prepayId != null) {
            logExt.setPrepay_id(prepayId.toString());
        }
        logExt.setCreate_date(new Date());
        weixinPayNotifyLogMapper.insertSelective(logExt);
        return result;
    }

    private WxPayNativeOrderResult createNativeOrder(WxPayParamsVo paramsVo, WxPaySetting setting, HttpServletRequest request) throws Exception {
        WxPayConfig config = setting.getConfig();

        WxPayUnifiedOrderRequest orderRequest = WxPayUtils.createRequest(paramsVo, request);
        orderRequest.setOpenid(null);
        WxPayNativeOrderResult result = wxPayService.createOrder(orderRequest);

        WeixinPayNotifyLogExt logExt = new WeixinPayNotifyLogExt();
        logExt.setBiz_type(paramsVo.getBiz_type());
        logExt.setBiz_sid(paramsVo.getBiz_sid());
        logExt.setOut_trade_no(paramsVo.getOut_trade_no());
        logExt.setTrade_type(config.getTradeType());
        logExt.setApp_id(config.getAppId());
        logExt.setMch_id(config.getMchId());
        logExt.setUrl(result.getCodeUrl());
        logExt.setCreate_date(new Date());
        weixinPayNotifyLogMapper.insertSelective(logExt);
        return result;
    }

    private WxPayAppOrderResult createAppOrder(WxPayParamsVo paramsVo, WxPaySetting setting, HttpServletRequest request) throws Exception {
        WxPayConfig config = setting.getConfig();

        WxPayUnifiedOrderRequest orderRequest = WxPayUtils.createRequest(paramsVo, request);
        orderRequest.setOpenid(null);
        WxPayAppOrderResult result = wxPayService.createOrder(orderRequest);

        WeixinPayNotifyLogExt logExt = new WeixinPayNotifyLogExt();
        logExt.setBiz_type(paramsVo.getBiz_type());
        logExt.setBiz_sid(paramsVo.getBiz_sid());
        logExt.setOut_trade_no(paramsVo.getOut_trade_no());
        logExt.setTrade_type(config.getTradeType());
        logExt.setApp_id(result.getAppId());
        logExt.setMch_id(result.getPartnerId());
        logExt.setPrepay_id(result.getPrepayId());
        logExt.setNonce_str(result.getNonceStr());
        logExt.setCreate_date(new Date());
        weixinPayNotifyLogMapper.insertSelective(logExt);
        return result;
    }
}
